using DataServer.ApplicationLib.Repositories;
using DataServer.ApplicationLib.Services;
using DataServer.CommonLib.Constants;
using FluentValidation.AspNetCore;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Serilog;
using Serilog.Events;
using Serilog.Formatting.Json;
using Swimj.AspNetCore.Infrastructure.Auth;
using Swimj.AspNetCore.Options;
using System.Reflection;
using System.Security.Claims;
using System.Text;

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
    .Enrich.FromLogContext()
    .WriteTo.Console()
    .CreateBootstrapLogger();

Log.Information("Starting web host");

try
{
    var builder = WebApplication.CreateBuilder(args);

    builder.Host.UseSerilog((context, services, configuration) => configuration
        .ReadFrom.Configuration(context.Configuration)
        .ReadFrom.Services(services)
        .Enrich.FromLogContext()
        .WriteTo.Console(new JsonFormatter()));

    // Add services to the container.

    var defaultConnStr = builder.Configuration.GetConnectionString("Default");
    
    builder.Services.AddHttpContextAccessor();

    builder.Services.AddCurrentUser();

    // 添加身份验证
    builder.Services
        .AddAuthentication(authentication =>
        {
            authentication.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            authentication.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(configureOptions =>
        {
            var key = builder.Configuration.GetValue<string>(
                $"SecuritySettings:{nameof(JwtSettings)}:{nameof(JwtSettings.Key)}");

            configureOptions.RequireHttpsMetadata = false;
            configureOptions.SaveToken = true;
            configureOptions.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)),
                ValidateIssuer = false,
                ValidateAudience = false,
                RoleClaimType = ClaimTypes.Role,
                ClockSkew = TimeSpan.Zero
            };
        });


    builder.Services.AddAuthorization(options =>
    {
        // Here I stored necessary permissions/roles in a constant
        foreach (var prop in typeof(ApiRoutes).GetNestedTypes().SelectMany(c =>
                     c.GetFields(BindingFlags.Public | BindingFlags.Static |
                                 BindingFlags.FlattenHierarchy)))
        {
            options.AddPolicy(prop.Name,
                policy => policy.RequireClaim("Permission",
                    prop.Name));
        }
    });

    builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());

    //builder.Services.AddAssemblies(AppDomain.CurrentDomain.GetAssemblies());

    builder.Services.AddScoped<IDataBaseRepository, DataBaseRepository>();
    builder.Services.AddScoped<IDataConfigRepository, DataConfigRepository>();
    builder.Services.AddScoped<IConfigFieldRepository, ConfigFieldRepository>();
    builder.Services.AddScoped<IConfigRuleRepository, ConfigRuleRepository>();
    builder.Services.AddScoped<IConfigDataRepository, ConfigDataRepository>();
    builder.Services.AddScoped<IConfigDataCheckRepository, ConfigDataCheckRepository>();
    builder.Services.AddScoped<IDataSourceRepository, DataSourceRepository>();
    builder.Services.AddScoped<IDimensionRepository, DimensionRepository>();
    builder.Services.AddScoped<IDimensionValuesRepository, DimensionValuesRepository>();

    builder.Services.AddScoped<IDatabaseService, DataBaseService>();
    builder.Services.AddScoped<IDataConfigService, DataConfigService>();
    builder.Services.AddScoped<IConfigDataService, ConfigDataService>();
    builder.Services.AddScoped<IDataSourceService, DataSourceService>();
    builder.Services.AddScoped<IDimensionService, DimensionService>();

    builder.Services.AddControllers();

    builder.Services.AddCors(options =>
    {
        options.AddPolicy("CorsPolicy",
            corsPolicyBuilder => corsPolicyBuilder
                .SetIsOriginAllowed(_ => true)
                .AllowAnyMethod()
                .AllowAnyHeader()
                .AllowCredentials());
    });

    builder.Services.AddFluentValidationAutoValidation();
    builder.Services.AddFluentValidationClientsideAdapters();

    // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen(c =>
    {
        c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
        {
            Name = "Authorization",
            In = ParameterLocation.Header,
            Type = SecuritySchemeType.ApiKey,
            Scheme = "Bearer",
            BearerFormat = "JWT",
            Description =
                "Input your Bearer token in this format - Bearer {your token here} to access this API",
        });
        c.AddSecurityRequirement(new OpenApiSecurityRequirement
        {
            {
                new OpenApiSecurityScheme
                {
                    Reference = new OpenApiReference
                    {
                        Type = ReferenceType.SecurityScheme,
                        Id = "Bearer",
                    },
                    Scheme = "Bearer",
                    Name = "Bearer",
                    In = ParameterLocation.Header,
                },
                new List<string>()
            },
        });
    });

    var app = builder.Build();

    // Configure the HTTP request pipeline.
    if (app.Environment.IsDevelopment())
    {
        app.UseSwagger();
        app.UseSwaggerUI();
    }

    //app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseCors("CorsPolicy");

    app.UseRouting();

    app.UseAuthentication();

    app.UseCurrentUser();

    app.UseAuthorization();

    app.MapControllers();

    app.Run();
}
catch (Exception e)
{
    Console.WriteLine(e);
    throw;
}